#include <utillib/util/synchronized.h>
#include <stdio.h>
#include <assert.h>

using namespace utillib::util;

/*
#ifdef _CHECK_DEAD_LOCK
Synchronized::LockManager::LockObjectHistory * Synchronized::LockManager::LockObjectHistory::m_thisInstance = NULL;
#endif
*/
Synchronized::LockObject::LockObject()	: m_readLockCount( 0 ), m_lockMode( LOCK_NO )
{
	CREATE_LOCKOBJECT( m_handle );
}

Synchronized::LockObject::~LockObject()
{
	DESTROY_LOCKOBJECT( m_handle );
}

void Synchronized::LockObject::acquire()
{
	ACQUIRE_LOCKOBJECT( m_handle );
	m_lockMode = LOCK_WRITE;
}

void Synchronized::LockObject::release()
{
	m_lockMode = LOCK_NO;
	RELEASE_LOCKOBJECT( m_handle );
}

void Synchronized::LockObject::acquireReadLock()
{
	if ( m_lockMode != LOCK_READ )
	{
		ACQUIRE_LOCKOBJECT( m_handle );
		m_lockMode = LOCK_READ;
	}
	InterlockedIncrement( &m_readLockCount );
	
}

void Synchronized::LockObject::releaseReadLock()
{
	LONG readLockCount = InterlockedDecrement( &m_readLockCount );
	if ( readLockCount == 0 )
		release();
}

#ifdef _CHECK_DEAD_LOCK
Synchronized::Synchronized( Synchronized::LockObject *lockObject , const char *filename , int line )
{
	Synchronized::LockManager::getInstance()->acquire( lockObject , filename, line );
#else
Synchronized::Synchronized( Synchronized::LockObject *lockObject  )
{
	lockObject->acquire();
#endif
	m_lockObject = lockObject;
}

Synchronized::~Synchronized()
{
#ifdef _CHECK_DEAD_LOCK
	Synchronized::LockManager::getInstance()->release( m_lockObject );
#else
	m_lockObject->release();
#endif

}


#ifdef _CHECK_DEAD_LOCK
Synchronized_Read::Synchronized_Read( Synchronized::LockObject *lockObject , const char *filename , int line )
{
	Synchronized::LockManager::getInstance()->acquireReadLock( lockObject , filename, line );
#else
Synchronized_Read::Synchronized_Read( Synchronized::LockObject *lockObject  )
{
	lockObject->acquireReadLock();
#endif
	m_lockObject = lockObject;
}

Synchronized_Read::~Synchronized_Read()
{
#ifdef _CHECK_DEAD_LOCK
	Synchronized::LockManager::getInstance()->releaseReadLock( m_lockObject );
#else
	m_lockObject->releaseReadLock();
#endif

}

#ifdef _CHECK_DEAD_LOCK
Synchronized::LockManager::LockManager() 
{
	m_tlsIndex = TlsAlloc();
}

Synchronized::LockManager::~LockManager()
{
	for ( int threadCount = 0 ; threadCount < m_lockObjectCollection.threadCount; threadCount++ )
	{
		SAFE_DELETE( m_lockObjectCollection.threadLockObject[ threadCount ] );
	}
	TlsFree( m_tlsIndex );
}

DWORD Synchronized::LockManager::getTlsIndex()
{
	if ( m_tlsIndex == 0xFFFFFFFF )
	{
		assert( false );
	}
	return ( m_tlsIndex );
}

void Synchronized::LockManager::acquire( Synchronized::LockObject *so , const char *file, int line )
{
	LOCKOBJECT_HISTORY_INSTANCE->m_tryEnterLockObject = so;
	strcpy( LOCKOBJECT_HISTORY_INSTANCE->m_tryEnterLockFileName, file );
	LOCKOBJECT_HISTORY_INSTANCE->m_tryEnterLockFileLine = line;

	if ( checkDeadLock( LOCKOBJECT_HISTORY_INSTANCE ) == NULL )			// possible deadlock 
		writeDump();
	else
	{
		so->acquire();
		LOCKOBJECT_HISTORY_INSTANCE->pushBackLockObject( so , file, line );
	}
	LOCKOBJECT_HISTORY_INSTANCE->m_tryEnterLockObject = NULL;
}

void Synchronized::LockManager::release( Synchronized::LockObject *so )
{
	if (LOCKOBJECT_HISTORY_INSTANCE->popBackLockObject( so ) == true )
		so->release();
}

void Synchronized::LockManager::acquireReadLock( Synchronized::LockObject *so , const char *file, int line )
{
	LOCKOBJECT_HISTORY_INSTANCE->m_tryEnterLockObject = so;
	strcpy( LOCKOBJECT_HISTORY_INSTANCE->m_tryEnterLockFileName, file );
	LOCKOBJECT_HISTORY_INSTANCE->m_tryEnterLockFileLine = line;

	if ( checkDeadLock( LOCKOBJECT_HISTORY_INSTANCE ) == NULL )			// possible deadlock 
		writeDump();
	else
	{
		so->acquireReadLock();
		LOCKOBJECT_HISTORY_INSTANCE->pushBackLockObject( so , file, line );
	}
	LOCKOBJECT_HISTORY_INSTANCE->m_tryEnterLockObject = NULL;
}

void Synchronized::LockManager::releaseReadLock( Synchronized::LockObject *so )
{
	if (LOCKOBJECT_HISTORY_INSTANCE->popBackLockObject( so ) == true )
		so->releaseReadLock();
}


Synchronized::LockManager::LockObjectHistory *Synchronized::LockManager::checkDeadLock( Synchronized::LockManager::LockObjectHistory* lockObjectHistory )
{
	if ( lockObjectHistory->getLockDepth() <= 0 ) return ( lockObjectHistory );

	for ( int tCount = 0 ; tCount < m_lockObjectCollection.threadCount; tCount++ )
	{
		LockObjectHistory *checkLockObjectHistory = m_lockObjectCollection.threadLockObject[ tCount ];
		if ( checkLockObjectHistory && ( checkLockObjectHistory != lockObjectHistory ) )
		{
			if  ( checkLockObjectHistory->findLockObject( lockObjectHistory->m_tryEnterLockObject ) == true )
			{
				if ( checkLockObjectHistory == LOCKOBJECT_HISTORY_INSTANCE ) return ( NULL );
				if ( lockObjectHistory->findLockObject( checkLockObjectHistory->m_tryEnterLockObject ) == true )
					return ( NULL );
				return ( checkDeadLock( checkLockObjectHistory ) );
			}
		}
	}
	return ( lockObjectHistory );
}

void Synchronized::LockManager::insertThread( LockObjectHistory *enterSeciontObjectInfo )
{
	if ( m_lockObjectCollection.threadCount >= _MAX_THREAD_ ) return;
	enterSeciontObjectInfo->m_threadID = ::GetCurrentThreadId();
	m_lockObjectCollection.threadLockObject[ m_lockObjectCollection.threadCount++ ] = enterSeciontObjectInfo;
}

void Synchronized::LockManager::writeDump()
{
	char dumpFileName[ _MAX_PATH ];
	SYSTEMTIME currentTime;
	GetLocalTime( &currentTime );

	wsprintf( dumpFileName, "deadlock_%02d_%02d_%02d_%02d_%02d_%d.log", currentTime.wMonth, currentTime.wDay,
			currentTime.wHour, currentTime.wMinute, currentTime.wSecond  , GetCurrentThreadId() );

	FILE *fp = fopen( dumpFileName, "w+" );
	if ( fp == NULL ) return;

	for ( int tCount = 0 ; tCount < m_lockObjectCollection.threadCount; tCount++ )
	{
		m_lockObjectCollection.threadLockObject[ tCount ]->writeDump( fp );
	}
	fclose( fp );
}

Synchronized::LockManager::LockObjectHistory::LockObjectHistory() : m_tryEnterLockObject( NULL ), m_lockDepth( 0 )
{
}

Synchronized::LockManager::LockObjectHistory::~LockObjectHistory()
{
}

Synchronized::LockManager::LockObjectHistory *Synchronized::LockManager::LockObjectHistory::getInstance()
{
	LockObjectHistory *instance = ( LockObjectHistory * )TlsGetValue( Synchronized::LockManager::getInstance()->getTlsIndex() );
	if ( instance == NULL )
	{
		instance = new LockObjectHistory;
		TlsSetValue( Synchronized::LockManager::getInstance()->getTlsIndex(), instance );
		Synchronized::LockManager::getInstance()->insertThread( instance );
	}
	return ( instance );
}

bool Synchronized::LockManager::LockObjectHistory::findLockObject( Synchronized::LockObject *lockObject )
{
	for ( int depth = 0 ; depth < m_lockDepth; depth++ )
	{
		if ( m_listLockObject[ depth ] == lockObject ) return ( true );
	}
	return ( false );
}

bool Synchronized::LockManager::LockObjectHistory::isTryingLockObject( Synchronized::LockObject *lockObject )
{
	if ( m_tryEnterLockObject == lockObject ) return ( true );
	return ( false );
}

void Synchronized::LockManager::LockObjectHistory::pushBackLockObject( Synchronized::LockObject *lockObject, const char *filename , int line  )
{
	if ( m_lockDepth >= _MAX_LOCK_DEPTH_ ) return;

	m_listLockObject[ m_lockDepth ] = lockObject;
	strcpy( m_listLockFileName[ m_lockDepth ], filename );
	m_listLockFileLine[ m_lockDepth ] = line;

	m_lockDepth++;
}

bool Synchronized::LockManager::LockObjectHistory::popBackLockObject( Synchronized::LockObject *lockObject )
{
	if ( m_listLockObject[ m_lockDepth - 1 ] == lockObject )
	{
		m_listLockObject[ --m_lockDepth ] = NULL;
		return ( true );
	}
	return ( false );
}
void Synchronized::LockManager::LockObjectHistory::writeDump( FILE *fp )
{
	char buffer[ 1024 ];
	wsprintf( buffer, "Current ThreadID : %08x\r\n", m_threadID );
	fwrite( buffer, strlen( buffer ), 1, fp );

	if ( m_tryEnterLockObject )
	{
		wsprintf( buffer, "LockObject[%08x] File : %-20s Line : %5d\r\n", m_tryEnterLockObject
				, m_tryEnterLockFileName, m_tryEnterLockFileLine );
		fwrite( buffer, strlen( buffer ), 1, fp );
	}

	wsprintf( buffer, "Lock Stack Information Depth[%d]\r\n" , m_lockDepth );
	fwrite( buffer, strlen( buffer ), 1, fp );

	for ( int depth = 0 ; depth < m_lockDepth; depth++ )
	{
		wsprintf( buffer, "LockObject[%08x] File : %-20s Line : %5d\r\n", m_listLockObject[ depth ]
		, m_listLockFileName[ depth ], m_listLockFileLine[ depth ] );
		fwrite( buffer, strlen( buffer ), 1, fp );
	}
	fwrite( "\r\n", 2, 1, fp );
}

int Synchronized::LockManager::LockObjectHistory::getLockDepth()
{
	return ( m_lockDepth );
}
#endif

